/**
 * For the Bluehouse integration the inbound email should be in this form:
 
 <BluehouseMeeting>
      <Opportunity id="1234"/>
      <Attendance>
            <Host company="IBM" email="dcarroll@salesforce.com" name="Anthony"/>
            <Participant company="" email="djcarroll@gmail.com" name="[Guest] test "/>
      </Attendance>
      <InviteList>>
            <Invitee email="dcarroll@salesforce.com"/>
            <Invitee email="djcarroll@gmail.com"/>
      </InviteList>
      <FilesPresented>
            <File title="test.ppt"/>
            <File title="test.doc"/>
      </FilesPresented>
      <Activity link="https://bluehousisv.lotus.com/activities/service/html/mainpage#activitypage%2c5678"/>
</BluehouseMeeting>

 * Email services are automated processes that use Apex classes
 * to process the contents, headers, and attachments of inbound
 * email.
 */
global class MeetingEmailService implements Messaging.InboundEmailHandler {

    private AtomService activitiesService;
    private ActivitiesClient activitiesClient;
    private LotusLiveUser__c llUser;
    private LotusLiveConfig__c config {
    	get { 
    		if (config == null) {
    			config = [Select Id, Name, Protocol__c, LotusLiveServer__c, CompanyKey__c From LotusLiveConfig__c Limit 1];
    		}
    		return config;
    	}
    	set {}
    }

    global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
        Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
        
        result.success = true;
        
        try {
            XMLDom xdom = new XMLDom(email.plainTextBody);
            XMLDom.Element dom = xdom.ownerDocument();
            
 		    MeetingRecord mr = new MeetingRecord(dom.getElementByTagName('Opportunity').getAttribute('id'));
			getHost(mr, dom.getElementByTagName('Attendance'));
            getInvitees(mr, dom.getElementByTagName('InviteList'));
            getFiles(mr, dom.getElementByTagName('FilesPresented'));
			Feed aFeed = getActivityDetails(mr, dom.getElementByTagName('Activity'));
            Task myTask = getTask(mr);
            MeetingReport__c mrpt = saveMeetingReport(mr, aFeed, myTask);
            updateMeetingTask(myTask, mr, mrpt);
            
        } catch (Exception e) {
            result.success = false;
            result.message = e.getMessage();
        }
        return result;
    }

    private void LookupUsernamePassword(User u) {
    	System.debug(LoggingLevel.INFO, '\n\nLooking for the host LotusLiveUser Record using ID: ' + u.Id + '\n' + u + '\n');
    	llUser = [Select Name, Password__c From LotusLiveUser__c Where User__c = :u.Id Limit 1];
    	System.debug(LoggingLevel.INFO, '\n\nLotusLiveUser Record: ' + llUser + '\n');
    }
    
    private void getHost(MeetingRecord mr, XMLDom.Element root) {
		XMLDom.Element attendance = root.getElementByTagName('Attendance');
        if (attendance != null) {
        	XMLDom.Element host = attendance.getElementByTagName('Host');
            mr.host = getHostUser(host);
            lookupUsernamePassword(mr.host);
            getParticipants(mr, attendance);
        }
    }
    
    private Feed getActivityDetails(MeetingRecord mr, XMLDom.Element root) {
    	Feed afeed;
        if (root != null) {
            String activityLink = root.getAttribute('link');
            mr.activityLink = activityLink.split(',')[0];
            mr.activityLinkAtom = activityLink.split(',')[1];
            mr.activityId = mr.activityLinkAtom.split('=')[1];
            afeed = getActivityFeed(mr.activityId); 
            mr.name = afeed.title;
        } else {
            mr.name = 'No Summary Available';
        }
        return afeed;
    }
    
    private Task getTask(MeetingRecord mr) {
	    if ([Select Count() From Task Where ActivityId__c = : mr.activityId] == 0) {
            return [Select Id, Status From Task Where Subject Like 'LotusLive Meeting%' and WhatId = :mr.opptyId and Status = 'In Progress' and CreatedById = :mr.host.Id and ActivityDate = :Date.today()];
        } else {
            return [Select Id, Status, ActivityTitle__c From Task Where ActivityId__c = :mr.activityId]; //Subject Like 'LotusLive Meeting%' and WhatId = :opptyId and Status = 'In Progress' and CreatedById = :mr.host.Id and ActivityDate = :Date.today()];
        }
    }
        	
    private List<ToDoItem> getToDos(Feed activityFeed) {
        List<Entry> entries = activityFeed.getEntries();
        List<ToDoItem> todos = new List<ToDoItem>();
        for (Entry entry : entries) {
            ToDoItem tdi = new ToDoItem(entry);
            todos.add(tdi);
        }
        return todos;
    }

    private void saveToDos(List<ToDoItem> todos, MeetingReport__c mrpt) {
        List<ToDo__c> todorecs = new List<ToDo__c>();
        for (ToDoItem tdi : todos) {
            todorecs.add(new ToDo__c(AtomId__c=tdi.atomId, Published__c=tdi.published, Updated__c=tdi.updated, ToDoLink__c=tdi.link, ToDoLinkAtom__c=tdi.linkAtom, name=tdi.title, DueDate__c = tdi.duedate, AssignedTo__c = tdi.assignedTo, Meeting_Report__c=mrpt.Id));
        }
        insert todorecs;
    }

    private Feed getActivityFeed(String Uuid) {
 		activitiesService = new AtomService(config.Protocol__c, config.LotusLiveServer__c, llUser.name, llUser.password__c);    
        activitiesClient = new ActivitiesClient(activitiesService);
        return activitiesClient.getActivityByUuid(Uuid);
    }
    
    private MeetingReport__c saveMeetingReport(MeetingRecord mr, Feed activityFeed, Task myTask) {

        MeetingReport__c mrpt = new MeetingReport__c(Opportunity__c=mr.opptyId);
        mrpt.Host__c = mr.host.Id;
        mrpt.Activity__c = mr.activityLink;
        mrpt.ActivityAtom__c = mr.activityLinkAtom;
        mrpt.ActivityId__c = mr.activityId;
        try {
            mrpt.Name = myTask.ActivityTitle__c;
        } catch(Exception e) {
            System.debug(LoggingLevel.ERROR, '\n\nError setting name on meeting report...\n' + e.getMessage() + '\n');
        }
         
        mrpt.ActivityTitle__c = activityFeed.title;
        List<ToDoItem> todos = getToDos(activityFeed);
        
        insert mrpt;
        saveToDos(todos, mrpt);
        
        mr.recordId = mrpt.Id;
        mr.name = [Select Name From MeetingReport__c Where Id = :mrpt.Id].Name;
        
        List<MeetingInvitees__c> mi = new List<MeetingInvitees__c>();
        List<MeetingAttendees__c> ma = new List<MeetingAttendees__c>();
        List<MeetingFile__c> files = new List<MeetingFile__c>();
        
        //Attendees
        for (String key : mr.c_participants.keySet()) {
            Contact c = mr.c_participants.get(key);
            ma.add(new MeetingAttendees__c(Name = c.Name, Type__c = 'Contact', Contact__c = c.Id, Meeting_Report__c = mrpt.Id));
        }
        for (String key : mr.u_participants.keySet()) {
            User u = mr.u_participants.get(key);
            ma.add(new MeetingAttendees__c(Name = u.Name, Type__c = 'User', User__c = u.Id, Meeting_Report__c = mrpt.Id));
        }
        for (String key : mr.l_participants.keySet()) {
            Lead l = mr.l_participants.get(key);
            ma.add(new MeetingAttendees__c(Name = l.Name, Type__c = 'Lead', Lead__c = l.Id, Meeting_Report__c = mrpt.Id));
        }
        
        //Invitees
        for (String key : mr.c_invitees.keySet()) {
            Contact c = mr.c_invitees.get(key);
            mi.add(new MeetingInvitees__c(Name = c.Name, Type__c = 'Contact', Contact__c = c.Id, Meeting_Report__c = mrpt.Id));
        }
        for (String key : mr.u_invitees.keySet()) {
            User u = mr.u_invitees.get(key);
            mi.add(new MeetingInvitees__c(Name = u.Name, Type__c = 'User', User__c = u.Id, Meeting_Report__c = mrpt.Id));
        }
        for (String key : mr.l_invitees.keySet()) {
            Lead l = mr.l_invitees.get(key);
            mi.add(new MeetingInvitees__c(Name = l.Name, Type__c = 'Lead', Lead__c = l.Id, Meeting_Report__c = mrpt.Id));
        } 
        
        //Files
        for (String file : mr.filesPresented) {
            files.add(new MeetingFile__c(Name = file, Meeting_Report__c = mrpt.Id));
        }
        insert ma;
        insert mi;
        insert files;
        return mrpt;
    }
    
    private void updateMeetingTask(Task myTask, MeetingRecord mr, MeetingReport__c mrpt) {
        myTask.Status = 'Completed';
        myTask.MeetingRecordId__c = mr.recordId;
        myTask.MeetingReportName__c = mr.name;
        myTask.ActivityId__c = mr.activityId;
        myTask.ActivityLinkUrl__c = mrpt.Activity__c;
        update myTask;
    }
    
    private Boolean personExists(String ptype, String email) {
        Integer personCount = 0;

        if (ptype == 'user') {
            personCount = [Select Count() From User Where Email = :email];
        } else if (ptype == 'lead') {
            personCount = [Select Count() From Lead Where Email = :email];
        } else if (ptype == 'contact') {
            personCount = [Select Count() From Contact Where Email = :email];
        }
        return (personCount == 1);
    }
    
    private User getUserByEmail(String email) {
        return [Select Id, Name From User Where Email = :email];
    }
    
    private Lead getLeadByEmail(String email) {
        return [Select Id, Name From Lead Where Email = :email];
    }
    
    private Contact getContactByEmail(String email) {
        return [Select Id, Name From Contact Where Email = :email];
    }
    
    private User getHostUser(XMLDom.Element hostEl) {
        
        String email = hostEl.getAttribute('email');
        if (personExists('user', email)) {
            return getUserByEmail(email);
        } else {
            return getUserByEmail('dcarroll@salesforce.com');
        }
    }
    
    private void getParticipants(MeetingRecord mr, XMLDom.Element attendance) {
        mr.u_participants = new Map<String, User>();
        mr.l_participants = new Map<String, Lead>();
        mr.c_participants = new Map<String, Contact>();
        
        List<XMLDom.Element> participants = attendance.getElementsByTagName('Participant');
        if (participants != null) {     
            for (XMLDom.Element participant : participants) {
                String email = participant.getAttribute('email');
                if (personExists('user', email)) {
                    mr.u_participants.put(email, getUserByEmail(email));
                } else if (personExists('lead', email)) {
                    mr.l_participants.put(email, getLeadByEmail(email));
                } else if (personExists('contact', email)) {
                    mr.c_participants.put(email, getContactByEmail(email));
                }
            }
        }
    }
    
    private void getInvitees(MeetingRecord mr, XMLDom.Element inviteList) {
        mr.u_invitees = new Map<String, User>();
        mr.l_invitees = new Map<String, Lead>();
        mr.c_invitees = new Map<String, Contact>();
        
        List<XMLDom.Element> invitees = inviteList.getElementsByTagName('Invitee');
        
        if (invitees != null) {
            for (XMLDom.Element invitee : invitees) {
                String email = invitee.getAttribute('email');
                if (personExists('user', email)) {
                    mr.u_invitees.put(email, getUserByEmail(email));
                } else if (personExists('lead', email)) {
                    mr.l_invitees.put(email, getLeadByEmail(email));
                } else if (personExists('contact', email)) {
                    mr.c_invitees.put(email, getContactByEmail(email));
                }
            }
        }
    }
    
    private void getFiles(MeetingRecord mr, XMLDom.Element fileList) {
        mr.filesPresented = new List<String>();
        if (fileList != null) {
            List<XMLDom.Element> files = fileList.getElementsByTagName('File'); 
            for (XMLDom.Element file : files) {
                mr.filesPresented.add(file.getAttribute('title'));
            }
        }
    }
}